package queue_prog

import "math"
import "gitee.com/tfcolin/knapsack"

type ServiceAllocateData struct {
	ns int
	total_nu int
	least_total_nu int

	fc []float64
	least_nu []int
	lambda []float64
	mu []float64

	uf []knapsack.UtilFunction
}

func (sad * ServiceAllocateData) init_uf () {
	ns := sad.ns
	sad.uf = make ([]knapsack.UtilFunction, ns)
	for i := 0; i < ns; i ++ {
		id := i
		sad.uf[i] = func (ns int) float64 {
			ns += sad.least_nu[id]
			lu := sad.lambda[id] / sad.mu[id]
			rho := lu / float64(ns)
			p0 := 1.0 / sad.fc[ns] / (1 - rho) * math.Pow (lu, float64(ns))
			for k := 0; k < ns; k ++ {
				p0 += math.Pow (lu, float64(k)) / sad.fc[k]
			}
			p0 = 1.0 / p0
			ql := math.Pow (float64(ns) * rho, float64(ns)) * rho * p0 / (sad.fc[ns] * math.Pow (1.0 - rho, 2.0))
			return -ql
		}
	}
}

func (sad * ServiceAllocateData) alloc_sad (ns int, total_nu int, lambda []float64, mu []float64) {
	sad.ns = ns
	sad.total_nu = total_nu
	sad.fc = make ([]float64, total_nu + 1)
	sad.fc[0] = 1
	for i := 1; i <= total_nu; i ++ { 
		sad.fc[i] = sad.fc[i - 1] * float64(i)
	}

	sad.lambda = make ([]float64, ns)
	sad.mu     = make ([]float64, ns)
	sad.least_nu = make ([]int, ns)
	copy (sad.lambda, lambda[:ns])
	copy (sad.mu, mu[:ns])
	for i := 0; i < ns; i ++ {
		sad.least_nu[i] = int(math.Ceil(lambda[i] / mu[i] + 1e-5))
		sad.least_total_nu += sad.least_nu[i]
	}

	if (sad.total_nu < sad.least_total_nu) {
		panic ("total number of unit is too small. (at least one queue cannot reach to static state)")
	}
}

func InitServiceAllocateData (ns int, total_nu int, lambda []float64, mu []float64) * ServiceAllocateData {
	var sad ServiceAllocateData
	sad.alloc_sad (ns, total_nu, lambda, mu)
	sad.init_uf ()
	return &sad
}

func (sad * ServiceAllocateData) GetUtilFunction (id int) knapsack.UtilFunction {
	return sad.uf[id]
}

func (sad * ServiceAllocateData) CalEQL (id int, nu int) float64 {
	if (nu < sad.least_nu[id]) {
		return 0
	} else {
		return - sad.GetUtilFunction (id) (nu - sad.least_nu[id])
	}
}

func (sad * ServiceAllocateData) ServiceAllocate () (nu []int, max_exp_qlen float64) {
	ns := sad.ns

	limit_coef := [][]int {make([]int, ns)}
	limit := []int {sad.total_nu - sad.least_total_nu}
	for i := 0; i < ns; i ++ {
		limit_coef[0][i] = 1
	}

	kd := knapsack.InitKnapsackData (knapsack.RM_MIN, 1, ns)

	for i := 0; i < ns; i ++ {
		kd.SetUtil (i, sad.uf[i])
	}
	kd.SetLimit (limit_coef, limit)
	kd.Solve ()

	nu = make ([]int, ns)
	for i := 0; i < ns; i ++ {
		nu[i] = kd.GetFinalSelect(i) + sad.least_nu[i]
	}

	max_exp_qlen = - kd.GetFinalValue()

	return 
}

